/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_ui.htm
 *
 * Zhiqim UI is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
 
+(function(Z)
{
//BEGIN

Z.MD5 = Z.Class.newInstance();
Z.MD5.V   = [1732584193, -271733879, -1732584194, 271733878];
Z.MD5.S   = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21];
Z.MD5.T   = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 
            0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 
            0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 
            0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 
            0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 
            0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 
            0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 
            0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391];
Z.MD5.PAD = 0x80;

/****************************************/
//定义Z.MD5下的原型属性和方法
/****************************************/
Z.MD5.prototype = 
{
    defaults:{
        abcd: Z.MD5.V
    },
    
    digest: function(bytes)
    {
        var i;
        
        //第一步，计算分组数目，每64字节一组，+8表示最后8个字节放原始字节长度，当bytes.length/64=0时要强制增加一组，其他情况补齐即可
        var num = Math.floor((bytes.length + 8) / 64) + 1;
        var pad = num * 64 - 8 - bytes.length - 1;
        
        //第二步，按要求建立新的分组字节数组，分别把原始数据、补位码和原始数据大小按要求小端格式设置好
        var buffer = new Int8Array(64 * num);
        for (i=0;i<bytes.length;i++){//字节拷贝
            buffer[i] = bytes[i];
        }
        buffer[bytes.length] = Z.MD5.PAD;//补码
        for (i=bytes.length+1;i<64*num;i++){//补0
            buffer[i] = 0;
        }

        //第三步，把字节数组转换成整数
        var m = new Int32Array(16 * num);
        for (i=0;i<16*num;i++){
            m[i] = this.getInt(buffer, i * 4);
        }
        m[16*num-2] = bytes.length * 8;//最后两位是原始码长度
        buffer = null;

        //第四步，按每组16个整数(64字节)进行更新abcd的数字签名
        for (i=0;i<num;i++){
            this.update(this.slice(m, i*16, (i+1)*16));
        }
        
        //第五步，把得到的abcd数字签名转化128位16个字节数组，返回结果
        var result = new Int8Array(16);
        this.putInt(result, 0, this.abcd[0]);
        this.putInt(result, 4, this.abcd[1]);
        this.putInt(result, 8, this.abcd[2]);
        this.putInt(result, 12, this.abcd[3]);
        return result;
    },
    
    /**
     * 分组更新abcd数字签名
     * 
     * @param x 一组16个整数(64字节)
     */
    update: function(x)
    {
        var a = this.abcd[0], b = this.abcd[1], c = this.abcd[2], d = this.abcd[3];

        // 第一轮
        a = this.ff(a, b, c, d, x[0],  Z.MD5.S[0],  Z.MD5.T[0]);
        d = this.ff(d, a, b, c, x[1],  Z.MD5.S[1],  Z.MD5.T[1]);
        c = this.ff(c, d, a, b, x[2],  Z.MD5.S[2],  Z.MD5.T[2]);
        b = this.ff(b, c, d, a, x[3],  Z.MD5.S[3],  Z.MD5.T[3]);
        a = this.ff(a, b, c, d, x[4],  Z.MD5.S[0],  Z.MD5.T[4]);
        d = this.ff(d, a, b, c, x[5],  Z.MD5.S[1],  Z.MD5.T[5]);
        c = this.ff(c, d, a, b, x[6],  Z.MD5.S[2],  Z.MD5.T[6]);
        b = this.ff(b, c, d, a, x[7],  Z.MD5.S[3],  Z.MD5.T[7]);
        a = this.ff(a, b, c, d, x[8],  Z.MD5.S[0],  Z.MD5.T[8]);
        d = this.ff(d, a, b, c, x[9],  Z.MD5.S[1],  Z.MD5.T[9]);
        c = this.ff(c, d, a, b, x[10], Z.MD5.S[2],  Z.MD5.T[10]);
        b = this.ff(b, c, d, a, x[11], Z.MD5.S[3],  Z.MD5.T[11]);
        a = this.ff(a, b, c, d, x[12], Z.MD5.S[0],  Z.MD5.T[12]);
        d = this.ff(d, a, b, c, x[13], Z.MD5.S[1],  Z.MD5.T[13]);
        c = this.ff(c, d, a, b, x[14], Z.MD5.S[2],  Z.MD5.T[14]);
        b = this.ff(b, c, d, a, x[15], Z.MD5.S[3],  Z.MD5.T[15]);
        // 第二轮
        a = this.gg(a, b, c, d, x[1],  Z.MD5.S[4],  Z.MD5.T[16]);
        d = this.gg(d, a, b, c, x[6],  Z.MD5.S[5],  Z.MD5.T[17]);
        c = this.gg(c, d, a, b, x[11], Z.MD5.S[6],  Z.MD5.T[18]);
        b = this.gg(b, c, d, a, x[0],  Z.MD5.S[7],  Z.MD5.T[19]);
        a = this.gg(a, b, c, d, x[5],  Z.MD5.S[4],  Z.MD5.T[20]);
        d = this.gg(d, a, b, c, x[10], Z.MD5.S[5],  Z.MD5.T[21]);
        c = this.gg(c, d, a, b, x[15], Z.MD5.S[6],  Z.MD5.T[22]);
        b = this.gg(b, c, d, a, x[4],  Z.MD5.S[7],  Z.MD5.T[23]);
        a = this.gg(a, b, c, d, x[9],  Z.MD5.S[4],  Z.MD5.T[24]);
        d = this.gg(d, a, b, c, x[14], Z.MD5.S[5],  Z.MD5.T[25]);
        c = this.gg(c, d, a, b, x[3],  Z.MD5.S[6],  Z.MD5.T[26]);
        b = this.gg(b, c, d, a, x[8],  Z.MD5.S[7],  Z.MD5.T[27]);
        a = this.gg(a, b, c, d, x[13], Z.MD5.S[4],  Z.MD5.T[28]);
        d = this.gg(d, a, b, c, x[2],  Z.MD5.S[5],  Z.MD5.T[29]);
        c = this.gg(c, d, a, b, x[7],  Z.MD5.S[6],  Z.MD5.T[30]);
        b = this.gg(b, c, d, a, x[12], Z.MD5.S[7],  Z.MD5.T[31]);
        // 第三轮
        a = this.hh(a, b, c, d, x[5],  Z.MD5.S[8],  Z.MD5.T[32]);
        d = this.hh(d, a, b, c, x[8],  Z.MD5.S[9],  Z.MD5.T[33]);
        c = this.hh(c, d, a, b, x[11], Z.MD5.S[10], Z.MD5.T[34]);
        b = this.hh(b, c, d, a, x[14], Z.MD5.S[11], Z.MD5.T[35]);
        a = this.hh(a, b, c, d, x[1],  Z.MD5.S[8],  Z.MD5.T[36]);
        d = this.hh(d, a, b, c, x[4],  Z.MD5.S[9],  Z.MD5.T[37]);
        c = this.hh(c, d, a, b, x[7],  Z.MD5.S[10], Z.MD5.T[38]);
        b = this.hh(b, c, d, a, x[10], Z.MD5.S[11], Z.MD5.T[39]);
        a = this.hh(a, b, c, d, x[13], Z.MD5.S[8],  Z.MD5.T[40]);
        d = this.hh(d, a, b, c, x[0],  Z.MD5.S[9],  Z.MD5.T[41]);
        c = this.hh(c, d, a, b, x[3],  Z.MD5.S[10], Z.MD5.T[42]);
        b = this.hh(b, c, d, a, x[6],  Z.MD5.S[11], Z.MD5.T[43]);
        a = this.hh(a, b, c, d, x[9],  Z.MD5.S[8],  Z.MD5.T[44]);
        d = this.hh(d, a, b, c, x[12], Z.MD5.S[9],  Z.MD5.T[45]);
        c = this.hh(c, d, a, b, x[15], Z.MD5.S[10], Z.MD5.T[46]);
        b = this.hh(b, c, d, a, x[2],  Z.MD5.S[11], Z.MD5.T[47]);
        // 第四轮
        a = this.ii(a, b, c, d, x[0],  Z.MD5.S[12], Z.MD5.T[48]);
        d = this.ii(d, a, b, c, x[7],  Z.MD5.S[13], Z.MD5.T[49]);
        c = this.ii(c, d, a, b, x[14], Z.MD5.S[14], Z.MD5.T[50]);
        b = this.ii(b, c, d, a, x[5],  Z.MD5.S[15], Z.MD5.T[51]);
        a = this.ii(a, b, c, d, x[12], Z.MD5.S[12], Z.MD5.T[52]);
        d = this.ii(d, a, b, c, x[3],  Z.MD5.S[13], Z.MD5.T[53]);
        c = this.ii(c, d, a, b, x[10], Z.MD5.S[14], Z.MD5.T[54]);
        b = this.ii(b, c, d, a, x[1],  Z.MD5.S[15], Z.MD5.T[55]);
        a = this.ii(a, b, c, d, x[8],  Z.MD5.S[12], Z.MD5.T[56]);
        d = this.ii(d, a, b, c, x[15], Z.MD5.S[13], Z.MD5.T[57]);
        c = this.ii(c, d, a, b, x[6],  Z.MD5.S[14], Z.MD5.T[58]);
        b = this.ii(b, c, d, a, x[13], Z.MD5.S[15], Z.MD5.T[59]);
        a = this.ii(a, b, c, d, x[4],  Z.MD5.S[12], Z.MD5.T[60]);
        d = this.ii(d, a, b, c, x[11], Z.MD5.S[13], Z.MD5.T[61]);
        c = this.ii(c, d, a, b, x[2],  Z.MD5.S[14], Z.MD5.T[62]);
        b = this.ii(b, c, d, a, x[9],  Z.MD5.S[15], Z.MD5.T[63]);

        this.abcd[0] = this.add(this.abcd[0], a);
        this.abcd[1] = this.add(this.abcd[1], b);
        this.abcd[2] = this.add(this.abcd[2], c);
        this.abcd[3] = this.add(this.abcd[3], d);
    },
    
    /***************************************************************************************/
    // 以下为MD5更新数字签名的非线性函数 
    /***************************************************************************************/
    
    /** a = b + ((a + q + x + t) << s) */
    cc: function(q, a, b, c, d, x, s, t)
    {
        a = this.add(this.add(a, q), this.add(x, t));
        a = (a << s) | (a >>> (32 - s));
        return this.add(a, b);
    },
    
    /** a = b + ((a + f(b,c,d) + x + t) << s) */
    ff: function(a, b, c, d, x, s, t)
    {
        return this.cc(this.f(b, c, d), a, b, c, d, x, s, t);
    },

    /** a = b + ((a + g(b,c,d) + x + t) << s) */
    gg: function(a, b, c, d, x, s, t)
    {
        return this.cc(this.g(b, c, d), a, b, c, d, x, s, t);
    },

    /** a = b + ((a + h(b,c,d) + x + t) << s) */
    hh: function(a, b, c, d, x, s, t)
    {
        return this.cc(this.h(b, c, d), a, b, c, d, x, s, t);
    },

    /** a = b + ((a + i(b,c,d) + x + t) << s) */
    ii: function(a, b, c, d, x, s, t)
    {
        return this.cc(this.i(b, c, d), a, b, c, d, x, s, t);
    },

    /** f(X ,Y ,Z) = (X & Y) | ((~X) & Z) */
    f: function(x, y, z)
    {
        return (x & y) | ((~x) & z);
    },

    /** g(X ,Y ,Z) = (X & Z) | (Y & (~Z)) */
    g: function(x, y, z)
    {
        return (x & z) | (y & (~z));
    },

    /** h(X ,Y ,Z) = X ^ Y ^ Z */
    h: function(x, y, z)
    {
        return x ^ y ^ z;
    },

    /** i(X ,Y ,Z) = Y ^ (X | (~Z)) */
    i: function(x, y, z)
    {
        return y ^ (x | (~z));
    },
    
    /***************************************************************************************/
    // 以下为MD5小端格式读写和安全add,slice方法
    /***************************************************************************************/
        
       /** getInt little-endian */
    getInt: function(b, off)
    {
        return ((b[off + 0] & 0xFF) << 0) + ((b[off + 1] & 0xFF) << 8)
            + ((b[off + 2] & 0xFF) << 16) + ((b[off + 3] & 0xFF) << 24);
    },
    
    /** putInt little-endian */
    putInt: function(b, off, val)
    {
        b[off + 0] = (val >>> 0);
        b[off + 1] = (val >>> 8);
        b[off + 2] = (val >>> 16);
        b[off + 3] = (val >>> 24);
    },
    
    /** 统一两值相加在16-bits下进行，以解决JS相加溢出的BUG */
       add: function(x, y)
    {
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    },
    
    /** 统一使用该方法，部分浏览器对TypeArray的slice支持不一致 */
    slice: function(m, start, end)
    {
        var len = end - start, index = 0;
        var x = new Int32Array(len);
        for (var i=start;i<end;i++) {
            x[index++] = m[i];
        }
        return x;
    },
};

/***************************************************************************************/
// 以下为MD5外部使用的方法
/***************************************************************************************/

/**
 * MD5编码，仅支持UTF-8,GBK等编码JS没有对照表不好转
 * 
 * @param src         字符串或字节数组
 * @return             目标串
 */
Z.MD5.encode = function(src)
{
    if (Z.T.isString(src))
        src = Z.S.toUTF8(src);
        
    var b = this.encodeToByte(src);
    return Z.S.toHexString(b);
};

    
/**
 * MD5编码,返回byte数组
 * 
 * @param src     字符串或字节数组
 * @return         目标字节数组
 */
Z.MD5.encodeToByte = function(src)
{
    if (Z.T.isString(src))
        src = Z.S.toUTF8(src);
        
    return new Z.MD5().digest(src);
};
    
//END
})(zhiqim);